Hacks实用工具篇-Hack1 在发布正式版前移除日志语句

使用ProGuard移除日志

作者:李旺成

时间:2016年5月26日

这个 Hack 将介绍一个移除日志语句的使用技巧。

常见 Log 管理方法

谁的项目里面不写几条日志,不打几条 Log?
好吧!至少我见过的项目里面或多或少都会出现 Log 语句,这可以说是开发之必备工具。那么,问题来了,你打 Log 倒是方便,一行代码即可,但是应用正式发布的时候可不希望有 Log 存在,很可能从 Log 中泄漏敏感信息。

对于这个问题,我见过的最常用的解决方案是,使用“DEBUG”标记,如果是开发环境就可以执行 Log 语句,如果不是则不会执行。

自定义 Logger 工具类

这里拿我以前用过的一个工具类来举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* Description: 企业中通用的Log管理
* 开发阶段LOGLEVEL = 6
* 发布阶段LOGLEVEL = -1
* @version 1.0
*/


public class Logger {

private static int LOGLEVEL = 6;
private static int VERBOSE = 1;
private static int DEBUG = 2;
private static int INFO = 3;
private static int WARN = 4;
private static int ERROR = 5;

public static void setDevelopMode(boolean flag) {
if(flag) {
LOGLEVEL = 6;
} else {
LOGLEVEL = -1;
}
}

public static void v(String tag, String msg) {
if(LOGLEVEL > VERBOSE && !TextUtils.isEmpty(msg)) {
Log.v(tag, msg);
}
}

public static void d(String tag, String msg) {
if(LOGLEVEL > DEBUG && !TextUtils.isEmpty(msg)) {
Log.d(tag, msg);
}
}

public static void i(String tag, String msg) {
if(LOGLEVEL > INFO && !TextUtils.isEmpty(msg)) {
Log.i(tag, msg);
}
}

public static void w(String tag, String msg) {
if(LOGLEVEL > WARN && !TextUtils.isEmpty(msg)) {
Log.w(tag, msg);
}
}

public static void e(String tag, String msg) {
if(LOGLEVEL > ERROR && !TextUtils.isEmpty(msg)) {
Log.e(tag, msg);
}
}

}

对系统提供的 Log 类做了一个很简单的封装,基本可以满足项目开发的需要。当然你可以使用 BuildConfig.DEBUG 来实现自动配置的功能。

强大的日志 logger

可能有不少开发者会使用 logger 来代替系统的日志工具,这个工具输出的日志确实很漂亮,功能也很强大,值得称赞,那我们来看看它是怎么控制日志输出开关的:

1
2
3
4
5
6
7
8
9
10
11
12
public enum LogLevel {

/**
* Prints all logs
*/

FULL,

/**
* No log will be printed
*/

NONE
}

对, logger 提供了两个日志级别,当日志级别设置为 NONE 的时候,所有的日志都不会输出。

使用 ProGuard 移除日志

上面举例的两种管理日志的方法,本质是相同的,它们确实都可以很简单的实现正式版本禁止输出日志的功能。但是,不知道有没有发现这些方法的缺陷。

常规方法的缺陷

这里列举一下我认为上述方式的缺陷,欢迎补充:

  1. 不管什么版本,日志输出语句依然存在
  2. 运行时需要做多余的判断,效率降低

更优的方案

我认为,最佳的效果是,开发的时候日志正常输入,发布的时候日志语句完全从编译出来的文件中移除。

当然,这里不能考虑手动删除或者注释,这对程序员来说简直是不能忍受的…很幸运我们有 ProGuard 可以完成这个功能。

这里不打算对 ProGuard 展开做介绍,我们就看看如何使用 ProGuard 实现在正式版本中移除 Log 语句。

注意:下面的操作基于 AndroidStudio 1.5。

1、开启 ProGuard 功能
打开 Module 的 build.gradle 文件,将 minifyEnabled 的值修改为 true:

开启ProGuard

2、移除日志
在 proguard-rules.pro 文件中添加如下代码:

1
2
3
4
5
6
7
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}

上述代码表示:移除所有使用 android.util.Log 类中 *() 方法的地方,不管这个方法的参数和返回类型是什么。

小结

ProGuard 很强大,这里提供的移除日志功能,仅仅是抛砖引玉,还有很多技巧可以探索。

坚持原创技术分享,您的支持将鼓励我继续创作!